home *** CD-ROM | disk | FTP | other *** search
/ The CICA Windows Explosion! / The CICA Windows Explosion! - Disc 2.iso / programr / upc12bs1.zip / UUCICO / comm.asm < prev    next >
Assembly Source File  |  1993-05-30  |  52KB  |  1,663 lines

  1.     TITLE    COMM
  2.     PAGE    83,132
  3. ;    $Id: comm.asm 1.7 1993/05/31 01:39:23 ahd Exp $
  4. ;
  5. ;    $Log: comm.asm $
  6. ;; Revision 1.7  1993/05/31  01:39:23  ahd
  7. ;; Add return to spin loop (fix by Bill Plummer)
  8. ;; Drop FIFO size to 8
  9. ;;
  10. ;; Revision 1.4  1993/05/30  00:20:02  ahd
  11. ;; Insert minor delay to allow slow modems to catch up
  12. ;;
  13. ;; Revision 1.2  1992/12/18  12:08:25  ahd
  14. ;; Add Plummer's fix for bad TASM assemble of com_errors
  15. ;;
  16. ;
  17. ; 14-Jun-93 plummer    Add RET to spinloop routine
  18. ; 14-Jun-93 plummer    Set FIFO thresholds to 8 rather than 16 bytes
  19. ; 18-May-93 plummer    Define IO$DELAY and use in UART type determination
  20. ; 16-May-93 plummer    Debug code to printout UART type
  21. ; 22-Apr-93 plummer    Make case consistent in "TBuff" so it links properly
  22. ;  2-Dec-92 plummer    Fix com_errors() again.  Change got lost.
  23. ; Fix com_errors() to avoid problems with tasm.  Plummer, 11/16/92
  24. ; 8259 EOI issued after interrupts serviced.  Plummer, 3/25/92
  25. ; Fix botch in Set_Baud.  Plummer, 3/20/92
  26. ; Put in Gordon Lee's cure from dropped interrupts.  Plummer, 3/19/92
  27. ; TEMPORARY ioctl_com().  Plummer, 3/9/92.
  28. ; Clear OUT2 bit in UART.  Some machines use it so enable IRQ. Plummer, 3/9/92
  29. ; Release send buffer if we can't assign a recv buffer.  Plummer, 3/9/92
  30. ; Move EQU's outside of SP_TAB struc definition.  ahd, 3/8/92.
  31. ; ahd changes: short jmp's out of range in INST, OPEN ???  (ahd, 3/?/92)
  32. ; open_com() leaves DTR unchanged so Drew's autobaud works.  Plummer, 3/2/92
  33. ; Missing DX load in close_com() -- FIFO mode not cleared.  Plummer, 3/2/92
  34. ; C calling convention does not require saving AX, BX, CX, DX. Plummer 2/23/92
  35. ; Flush consideration of the PC Jr.  Wm. W. Plummer, 2/15/92
  36. ; Cleanup PUSHF/POPF and CLI/STI useage.  Wm. W. Plummer, 2/15/92
  37. ; Make SENDII have Giles Todd's change.  Wm. W. Plummer, 2/15/92
  38. ; Changes to Giles Todd's code to support dynamic buffers.  Plummer, 2/3/92
  39. ; 26 Jan 92 Giles Todd    Prime THR for UARTs which do not give a Tx empty
  40. ;            interrupt when Tx interrupts are enabled.
  41. ; S_SIZE & R_SIZE may be set with -D to MASM.  Wm. W. Plummer, 1/19/92
  42. ; Assign buffers dynamically.  Wm. W. Plummer, 1/19/92
  43. ; Unfix byte length -- I screwed up.  Wm. W. Plummer, 1/15/92
  44. ; Fix byte length with specific PARITY select.    Wm. W. Plummer, 1/13/92
  45. ; Buffers up to 4096 per AHD.  Wm. W. Plummer, 1/1/92
  46. ; Always use FIFO length of 16 on send side.  Wm. W. Plummer, 12/30/91.
  47. ; Init DSR and CTS previous state from current status.    Wm. Plummer, 12/30/91.
  48. ; UUPC conditional to disable v.24.  Wm. W. Plummer, 12/30/91.
  49. ; Buffer sizes up to 2048 per ahd.  Wm. W. Plummer, 12/15/91.
  50. ; dtr_on() switches to D connection if CTS&DSR don't come up.  WWP, 12/15/91.
  51. ; New dtr_on() logic.  Wm. W. Plummer, 12/11/91
  52. ; Fix bad reg. per report from user.  Wm. W. Plummer, 12/11/91
  53. ; Semicolon before control-L's for MASM 5.00 per ahd. Wm. W. Plummer, 12/8/91
  54. ; Use AHD's handling of COM ports.  Wm. W. Plummer, 11/29/91
  55. ; Buffer sizes reduced and required to be 2**N.  Wm. W. Plummer, 11/11/91
  56. ; Accomodate V.24 requirements on DTR flaps.  Wm. W. Plummer 10/15/91
  57. ; Revised DTR_ON_COM to solve user problem.  Wm. W. Plummer, 10/3/91
  58. ; Make time delays independent of CPU speed.  Wm. W. Plummer, 9/16/91
  59. ; Use interrupts to trace CD, DSR, Wm. W. Plummer, 9/16/91
  60. ; Remove modem control from TXI. Wm. W. Plummer, 9/13/91
  61. ; Completely redo the XOFF/XON logic.  Too many races before. Wm. W. Plummer
  62. ; Revise interrupt dispatch for speed & function.  William W. Plummer, 9/12/91
  63. ; Merge in ahd's changes to flush control Q,S when received as flow control
  64. ; SEND buffer allows one byte for a SENDII call.  Avoids flow control
  65. ;  lockups. - William W. Plummer, 8/30/91
  66. ; Support for NS16550A chip with SILO - William W. Plummer, 8/30/91
  67. ; Add modem_status() routine - William W. Plummer, 7/2/91
  68. ; Put wrong code under AHD conditional - William W. Plummer, 7/2/91
  69. ; Change TITLE, repair bad instr after INST3 - William W. Plummer, 7/1/91
  70. ; Modified to use COM1 thru COM4 - William W. Plummer, 2/21/91
  71. ; Eliminate (incomplete) support for DOS1 - William W. Plummer, 11/13/90
  72.  
  73. ; Changes may be copied and modified with no notice.  Copyrights and copylefts
  74. ; are consider silly and do not apply.    --  William W. Plummer
  75.  
  76. ; modified to use MSC calling sequence.  jrr 3/86
  77. ;****************************************************************************
  78. ; Communications Package for the IBM PC, XT, AT and strict compatibles.
  79. ; May be copied and used freely -- This is a public domain program
  80. ; Developed by Richard Gillmann, John Romkey, Jerry Saltzer,
  81. ; Craig Milo Rogers, Dave Mitton and Larry Afrin.
  82. ;
  83. ; We'd sure like to see any improvements you might make.
  84. ; Please send all comments and queries about this package
  85. ; to GILLMANN@USC-ISIB.ARPA
  86. ;
  87. ; o Supports both serial ports simultaneously
  88. ; o All speeds to 19200 baud
  89. ; o Compatible with PC, XT, AT
  90. ; o Built in XON/XOFF flow control option
  91. ; o C language calling conventions
  92. ; o Logs all comm errors
  93. ; o Direct connect or modem protocol
  94.     PAGE;
  95. ;
  96. ; Buffer sizes -- *** MUST be powers of 2 ****
  97.  
  98. IFDEF UUPC
  99.     R_SIZE    EQU    4096
  100.     S_SIZE    EQU    4096
  101. ENDIF
  102.  
  103. ; If not set above, maybe on assembler command line.  But if not, ...
  104. IFNDEF R_SIZE
  105.     R_SIZE    EQU    512    ; Recv buffer size
  106. ENDIF
  107. IFNDEF S_SIZE
  108.     S_SIZE    EQU    512    ; Send buffer size
  109. ENDIF
  110.  
  111. ; INTERRUPT NUMBERS
  112. INT_COM1 EQU    0CH        ; COM1: FROM 8259
  113. INT_COM2 EQU    0BH        ; COM2: FROM 8259
  114. INT_COM3 EQU    0CH        ; COM3: FROM 8259
  115. INT_COM4 EQU    0BH        ; COM4: FROM 8259
  116. ; 8259 PORTS
  117. INTA00    EQU    20H        ; 8259A PORT, A0 = 0
  118. INTA01    EQU    21H        ; 8259A PORT, A0 = 1
  119. ; COM1: & COM3: LEVEL 4
  120. IRQ4    EQU    2*2*2*2     ; 8259A OCW1 MASK, M4=1, A0=0
  121. NIRQ4    EQU    NOT IRQ4 AND 0FFH ; COMPLEMENT OF ABOVE
  122. EOI4    EQU    4 OR 01100000B    ; 8259A OCW2 SPECIFIC IRQ4 EOI, A0=0
  123. ; COM2: & COM4: LEVEL 3
  124. IRQ3    EQU    2*2*2        ; 8259A OCW1 MASK, M3=1, A0=0
  125. NIRQ3    EQU    NOT IRQ3 AND 0FFH ; COMPLEMENT OF ABOVE
  126. EOI3    EQU    3 OR 01100000B    ; 8259A OCW2 SPECIFIC IRQ3 EOI, A0=0
  127.  
  128. ; FLOW CONTROL CHARACTERS
  129. CONTROL_Q EQU    11H        ; XON
  130. CONTROL_S EQU    13H        ; XOFF
  131. ; MISC.
  132. DOS    EQU    21H        ; DOS FUNCTION CALLS
  133.  
  134. ;
  135. ; ROM BIOS Data Area
  136. ;
  137. RBDA    SEGMENT AT 40H
  138. RS232_BASE DW    4 DUP(?)    ; ADDRESSES OF RS232 ADAPTERS
  139. RBDA    ENDS
  140.  
  141. PAGE;
  142. ;
  143. ; TABLE FOR EACH SERIAL PORT
  144. ;
  145. SP_TAB        STRUC
  146. PORT        DB    ?    ; 1 OR 2 OR 3 OR 4
  147. ; PARAMETERS FOR THIS INTERRUPT LEVEL
  148. INT_COM     DB    ?    ; INTERRUPT NUMBER
  149. IRQ        DB    ?    ; 8259A OCW1 MASK
  150. NIRQ        DB    ?    ; COMPLEMENT OF ABOVE
  151. EOI        DB    ?    ; 8259A OCW2 SPECIFIC END OF INTERRUPT
  152. ; INTERRUPT HANDLERS FOR THIS LEVEL
  153. INT_HNDLR    DW    ?    ; OFFSET TO INTERRUPT HANDLER
  154. OLD_COM_OFF    DW    ?    ; OLD HANDLER'S OFFSET
  155. OLD_COM_SEG    DW    ?    ; OLD HANDLER'S SEGMENT
  156. ; ATTRIBUTES
  157. INSTALLED    DB    ?    ; IS PORT INSTALLED ON THIS PC? (1=YES,0=NO)
  158. BAUD_RATE    DW    ?    ; 19200 MAX
  159. CONNECTION    DB    ?    ; M(ODEM), D(IRECT)
  160. PARITY        DB    ?    ; N(ONE), O(DD), E(VEN), S(PACE), M(ARK)
  161. STOP_BITS    DB    ?    ; 1, 2
  162. XON_XOFF    DB    ?    ; E(NABLED), D(ISABLED)
  163. ; FLOW CONTROL STATE
  164. HOST_OFF    DB    ?    ; HOST XOFF'ED (1=YES,0=NO)
  165. PC_OFF        DB    ?    ; PC XOFF'ED (1=YES,0=NO)
  166. URGENT_SEND    DB    ?    ; We MUST send one byte (XON/XOFF)
  167. SEND_OK     DB    ?    ; DSR and CTS are ON
  168. ; ERROR COUNTS
  169. ERROR_BLOCK    DW    8 DUP(?); EIGHT ERROR COUNTERS
  170.  
  171. ; UART PORTS - DATREG thru MSR must be in order shown.
  172. DATREG        DW    ?    ; DATA REGISTER
  173. IER        DW    ?    ; INTERRUPT ENABLE REGISTER
  174. IIR        DW    ?    ; INTERRUPT IDENTIFICATION REGISTER (RO)
  175. LCR        DW    ?    ; LINE CONTROL REGISTER
  176. MCR        DW    ?    ; MODEM CONTROL REGISTER
  177. LSR        DW    ?    ; LINE STATUS REGISTER
  178. MSR        DW    ?    ; MODEM STATUS REGISTER
  179. UART_SILO_LEN    DB    ?    ; Size of a silo chunk (1 for 8250)
  180. ;
  181. ; BUFFER POINTERS
  182. START_TDATA    DW    ?    ; INDEX TO FIRST CHARACTER IN X-MIT BUFFER
  183. END_TDATA    DW    ?    ; INDEX TO FIRST FREE SPACE IN X-MIT BUFFER
  184. START_RDATA    DW    ?    ; INDEX TO FIRST CHARACTER IN REC. BUFFER
  185. END_RDATA    DW    ?    ; INDEX TO FIRST FREE SPACE IN REC. BUFFER
  186. ; BUFFER COUNTS
  187. SIZE_TDATA    DW    ?    ; NUMBER OF CHARACTERS IN X-MIT BUFFER
  188. SIZE_RDATA    DW    ?    ; NUMBER OF CHARACTERS IN REC. BUFFER
  189. ; BUFFERS
  190. TBuff        DD    ?    ; Pointer to transmit buffer
  191. RBuff        DD    ?    ; Pointer to receive buffer
  192. SP_TAB        ENDS
  193.  
  194. ; SP_TAB EQUATES
  195. ; WE HAVE TO USE THESE BECAUSE OF PROBLEMS WITH STRUC
  196. EOVFLOW  EQU    ERROR_BLOCK    ; BUFFER OVERFLOWS
  197. EOVRUN        EQU    ERROR_BLOCK+2    ; RECEIVE OVERRUNS
  198. EBREAK        EQU    ERROR_BLOCK+4    ; BREAK CHARS
  199. EFRAME        EQU    ERROR_BLOCK+6    ; FRAMING ERRORS
  200. EPARITY  EQU    ERROR_BLOCK+8        ; PARITY ERRORS
  201. EXMIT        EQU    ERROR_BLOCK+10    ; TRANSMISSION ERRORS
  202. EDSR        EQU    ERROR_BLOCK+12    ; DATA SET READY ERRORS
  203. ECTS        EQU    ERROR_BLOCK+14    ; CLEAR TO SEND ERRORS
  204. DLL        EQU    DATREG        ; LOW DIVISOR LATCH
  205. DLH        EQU    IER        ; HIGH DIVISOR LATCH
  206.  
  207. ;
  208. ; Equates having to do with the FIFO
  209. ;
  210. FCR        EQU    IIR    ; FIFO Control Register (WO)
  211.  ; Bits in FCR for NS16550A UART.  Note that writes to FCR are ignored
  212.  ; by other chips.
  213.  FIFO_ENABLE    EQU    001H    ; Enable FIFO mode
  214.  FIFO_CLR_RCV    EQU    002H    ; Clear receive FIFO
  215.  FIFO_CLR_XMT    EQU    004H    ; Clear transmit FIFO
  216.  FIFO_STR_DMA    EQU    008H    ; Start DMA Mode
  217.  ; 10H and 20H bits are register bank select on some UARTs (not handled)
  218.  FIFO_SZ_4    EQU    040H    ; Warning level is 4 before end
  219.  FIFO_SZ_8    EQU    080H    ; Warning level is 8 before end
  220.  FIFO_SZ_14    EQU    0C0H    ; Warning level is 14 before end
  221.  ;
  222.  ; Commands used in code to operate FIFO.  Made up as combinations of above
  223.  ;
  224.  FIFO_CLEAR    EQU    0    ; Turn off FIFO
  225.  FIFO_SETUP    EQU    FIFO_SZ_8 OR FIFO_ENABLE
  226.  FIFO_INIT    EQU    FIFO_SETUP OR FIFO_CLR_RCV OR FIFO_CLR_XMT
  227.  ;
  228.  ; Miscellaneous FIFO-related stuff
  229.  ;
  230. FIFO_ENABLED    EQU    0C0H    ; 16550 makes these equal FIFO_ENABLE
  231. FIFO_LEN    EQU    8    ; Length of the transmit FIFO in a 16550A
  232.     PAGE;
  233. ;    put the data in the DGROUP segment
  234. ;    far calls enter with DS pointing to DGROUP
  235. ;
  236. DGROUP    GROUP _DATA
  237. _DATA    SEGMENT PUBLIC 'DATA'
  238. ;
  239. DIV50        DW    2304    ; ACTUAL DIVISOR FOR 50 BAUD IN USE
  240. CURRENT_AREA    DW    AREA1    ; CURRENTLY SELECTED AREA
  241. ; DATA AREAS FOR EACH PORT
  242. AREA1    SP_TAB    <1,INT_COM1,IRQ4,NIRQ4,EOI4>    ; COM1 DATA AREA
  243. AREA2    SP_TAB    <2,INT_COM2,IRQ3,NIRQ3,EOI3>    ; COM2 DATA AREA
  244. AREA3    SP_TAB    <3,INT_COM3,IRQ4,NIRQ4,EOI4>    ; COM3 DATA AREA
  245. AREA4    SP_TAB    <4,INT_COM4,IRQ3,NIRQ3,EOI3>    ; COM4 DATA AREA
  246.  
  247. IFDEF DEBUG
  248.  ST8250:    DB "8250$"
  249.  ST16550:    DB "16550$"
  250.  STUART:    DB " UART detected", 0DH, 0AH, '$'
  251. ENDIF
  252.  
  253. _DATA    ENDS
  254.  
  255.  
  256.  
  257.  
  258. COM_TEXT SEGMENT PARA PUBLIC 'CODE'
  259.      ASSUME CS:COM_TEXT,DS:DGROUP,ES:NOTHING
  260.  
  261.      PUBLIC AREA1, AREA2, AREA3, AREA4
  262.      PUBLIC _select_port
  263.      PUBLIC _save_com
  264.      PUBLIC _install_com
  265.      PUBLIC _restore_com
  266.      PUBLIC _open_com
  267.      PUBLIC _ioctl_com
  268.      PUBLIC _close_com
  269.      PUBLIC _dtr_on
  270.      PUBLIC _dtr_off
  271.      PUBLIC _r_count
  272.      PUBLIC _s_count
  273.      PUBLIC _receive_com
  274.      PUBLIC _send_com
  275.      PUBLIC _sendi_com
  276. IFNDEF UUPC
  277.      PUBLIC _send_local
  278. ENDIF
  279.      PUBLIC _break_com
  280.      PUBLIC _com_errors
  281.      PUBLIC _modem_status
  282. IFDEF DEBUG
  283.      PUBLIC INST2, INST4
  284.      PUBLIC OPEN1, OPEN2, OPENX
  285.      PUBLIC DTRON1, DTRON6, DTRONF, DTRONS, DTRONX
  286.      PUBLIC RECV1, RECV3, RECV4, RECVX
  287.      PUBLIC SEND1, SENDX
  288.      PUBLIC WaitN, WaitN1, WaitN2
  289.      PUBLIC SENDII, SENDII2, SENDII4, SENDIIX
  290.      PUBLIC SPINLOOP
  291.      PUBLIC CHROUT, CHROUX
  292.      PUBLIC BREAKX
  293.      PUBLIC INT_HNDLR1, INT_HNDLR2, INT_HNDLR3, INT_HNDLR4
  294.      PUBLIC INT_COMMON, REPOLL, INT_END
  295.      PUBLIC LSI
  296.      PUBLIC MSI
  297.      PUBLIC TXI, TXI1, TXI2, TXI3, TXI9
  298.      PUBLIC TX_CHR
  299.      PUBLIC RXI, RXI0, RXI1, RXI2, RXI6, RXIX
  300. ENDIF
  301.     PAGE;
  302. ; Notes, thoughts and explainations by Bill Plummer.  These are intended to
  303. ; help those of you who would like to make modifications.
  304.  
  305. ; Here's the order of calls in UUPC.  The routines in COMM.ASM are called
  306. ; from ulib.c.
  307.  
  308. ; First (when a line in system has been read?), ulib&openline calls
  309. ;     select_port()        ; Sets up CURRENT_AREA
  310. ; then,  save_com()        ; Save INT vector
  311. ; then,  install_com()        ; Init area, hook INT
  312. ; then,  open_com(&cmd, 'D', 'N', STOP*T, 'D')  ; Init UART, clr bufs
  313. ; then,  dtr_on().
  314.  
  315. ; At that point the line is up and running.  UUPC calls ulib&sread()
  316. ; which calls,    receive_com();
  317.  
  318. ; And UUPC calls ulib&swrite()
  319. ; which calls,    send_com();
  320.  
  321. ; To cause an error that the receiver will see, UUPC calls ulib&ssendbrk();
  322. ; which calls,    break_com();
  323.  
  324. ; When all done with the line, UUPC calls ulib&closeline()
  325. ; which calls,    dtr_off();
  326. ; then,  close_com();
  327. ; then,  restore_com();     ; Unhook INT
  328. ; and,     stat_errors();
  329.  
  330.  
  331. ; Note: On the PC COM1 and COM3 share IRQ4, while COM2 and COM4 share IRQ3.
  332. ; BUT, only one device on a given IRQ line can be active at a time.  So it is
  333. ; sufficient for UUPC to hook whatever IRQ INT its modem is on as long as it
  334. ; unhooks it when it is done with that COM port.  COMM cannot be an installed
  335. ; device driver since it must go away when UUPC is done so that other devices
  336. ; on the same INTs will come back to life.  Also, it is OK to have a static
  337. ; CURRENT_AREA containing the current area that UUPC is using.
  338.  
  339. ; Note about using the NS16550A UART chip's FIFOs.  These are operated as
  340. ; silos.  In other words when an interrupt happens because the receive(send)
  341. ; FIFO is nearly full(empty), as many bytes as possible are transferred and
  342. ; the interrupt dismissed.  Thus, the interrupt load is lowered.
  343.  
  344.  
  345. ; Concerning the way the comm line is brought up.
  346. ; There are two basic cases, the Direct ('D') connection and the Modem ('M')
  347. ; connection.  For either UUPC calls dtr_on_com() to bring up the line.  This
  348. ; causes Data Terminal Ready (DTR) and Request To Send (RTS) to be set.  Note
  349. ; this is OK for a simple 3-wire link but may be REQUIRED for a COM port
  350. ; connected to an external modem.
  351.  
  352. ; The difference between a D connection and an M connection is
  353. ; whether or not the PC can expect any signals back from the modem.  If
  354. ; there is a simple 3-wire link, Data Set Ready will be floating.
  355. ; (Actually, some wise people jumper Data Terminal Read back to Data
  356. ; Set Ready so the PC sees its own DTR appear as DSR.)    UUPC should be
  357. ; able to handle the simplest cable as a design feature.  So both D and
  358. ; M connections send out DTR and RTS, but only the M connection expects
  359. ; a modem to respond.
  360.  
  361. ; Then, if it is full modem connection (M), we wait for a few
  362. ; seconds hoping that both Data Set Ready (DSR) and Clear To Send (CTS)
  363. ; will come up.  If they don't, the associated counters are incremented
  364. ; for subsequent printing in the error log.  Note that no error is
  365. ; reported from COMM to UUPC at this point, although this would be a
  366. ; good idea.  COMMFIFO.ASM forces the connection to be a D type and lets
  367. ; UUPC storm ahead with its output trying to
  368. ; establish a link, but the output is never sent due to one of the
  369. ; control signals being false.    UUPC could check the modem status using
  370. ; a call which has been installed just for this purpose.
  371.  
  372. ; Note, if you are going to connect your PC running UUPC to,
  373. ; say, a mainframe and you need hardware flow control (i.e., RTS-CTS
  374. ; handshaking), use a Modem connection.  Using a simple 3-wire cable
  375. ; forbids hardware flow control and UUPC must be instructed to use a
  376. ; Direct connection.  Refer to comments in the SYSTEMS file on how to
  377. ; make this selection.
  378.  
  379. ; References used in designing the revisions to COMM.ASM:
  380. ;    1.    The UNIX fas.c Driver code.
  381. ;    2.    SLIP8250.ASM from the Clarkson driver set.
  382. ;    3.    NS16550A data sheet and AN-491 from National Semiconductor.
  383. ;    4.    Bell System Data Communications, Technical Reference for
  384. ;        Data Set 103A, Interface Specification, February, 1967
  385. ;    5.    Network mail regarding V.24
  386. ;    6.    Joe Doupnik
  387.     PAGE;
  388. ;
  389. ; void far select_port(int)
  390. ;    Arg is 1..4 and specifies which COM port is referenced by
  391. ;    all other calls in this package.
  392. ;
  393. _select_port PROC FAR
  394.     push bp
  395.     mov bp,sp
  396.     mov AX,[bp+6]            ; get aguement
  397.     CMP    AL,1            ; Port 1?
  398.      JE    SP1            ; Yes
  399.     CMP    AL,2            ; Port 2?
  400.      JE    SP2            ; Yes
  401.     CMP    AL,3            ; Port 3?
  402.      JE    SP3            ; Yes
  403.     CMP    AL,4            ; Port 4?
  404.      JE    SP4            ; Yes
  405.     INT 20H             ; N.O.T.A. ????? Halt for debugging!
  406.     ; Assume port 1 if continued
  407. SP1:    MOV    AX,OFFSET DGROUP:AREA1    ; SELECT COM1 DATA AREA
  408.     JMP    SHORT SPX        ; CONTINUE
  409. SP2:    MOV    AX,OFFSET DGROUP:AREA2    ; SELECT COM2 DATA AREA
  410.     JMP    SHORT SPX        ; CONTINUE
  411. SP3:    MOV    AX,OFFSET DGROUP:AREA3    ; SELECT COM3 DATA AREA
  412.     JMP    SHORT SPX        ; CONTINUE
  413. SP4:    MOV    AX,OFFSET DGROUP:AREA4    ; SELECT COM4 DATA AREA
  414.     ;Fall into SPX
  415. SPX:    MOV    CURRENT_AREA,AX     ; SET SELECTION IN MEMORY
  416.     mov sp,bp
  417.     pop bp
  418.     RET
  419. _select_port ENDP
  420.     PAGE;
  421. ;
  422. ; void far save_com(void)
  423. ;    Save the interrupt vector of the selected COM port.
  424. ;    N.B. save_com() and restore_com() call MUST be properly nested
  425. ;
  426. _save_com PROC FAR
  427.     push bp
  428.     mov bp,sp
  429.     PUSH SI
  430.     PUSH    ES            ; SAVE EXTRA SEGMENT
  431.     MOV    SI,CURRENT_AREA     ; SI POINTS TO DATA AREA
  432.     MOV    AREA1.INT_HNDLR,OFFSET INT_HNDLR1
  433.     MOV    AREA2.INT_HNDLR,OFFSET INT_HNDLR2
  434.     MOV    AREA3.INT_HNDLR,OFFSET INT_HNDLR3
  435.     MOV    AREA4.INT_HNDLR,OFFSET INT_HNDLR4
  436.  
  437. ; Save old interrupt vector
  438.     MOV    AH,35H            ; FETCH INTERRUPT VECTOR CONTENTS
  439.     MOV    AL,INT_COM[SI]        ; INTERRUPT NUMBER
  440.     INT    DOS            ; DOS 2 FUNCTION
  441.     MOV    OLD_COM_OFF[SI],BX    ; SAVE
  442.     MOV    BX,ES            ; ES:BX
  443.     MOV    OLD_COM_SEG[SI],BX    ; FOR LATER RESTORATION
  444.     POP    ES            ; RESTORE ES
  445.     POP SI
  446.     mov sp,bp
  447.     pop bp
  448.     RET                ; DONE
  449. _save_com ENDP
  450.     PAGE;
  451. ;
  452. ; int far install_com(void)
  453. ;
  454. ;    Install the selected COM port.
  455. ;    Returns:    0: Failure
  456. ;            1: Success
  457. ;
  458. ; SET UART PORTS FROM RS-232 BASE IN ROM BIOS DATA AREA
  459. ; INITIALIZE PORT CONSTANTS AND ERROR COUNTS
  460. ;
  461. ; Assign blocks of memory for transmit and receive buffers
  462. ;
  463. _install_com PROC FAR
  464.     push bp
  465.     mov bp,sp
  466.     PUSHF                ; Save caller's interrupt state
  467.     PUSH SI
  468.     PUSH DI
  469.     PUSH ES
  470.     MOV    SI,CURRENT_AREA     ; SI POINTS TO DATA AREA
  471.     CMP    INSTALLED[SI],1     ; Is port installed on this machine?
  472.      JNE    INST1            ; NO, CONTINUE
  473.     JMP    INST9            ; ELSE JUMP IF ALREADY INSTALLED
  474.  
  475. ; Assign memory for transmit and receive buffers
  476.  
  477. INST1:    MOV BX,S_SIZE            ; Send buffer size
  478.     ADD BX,0FH            ; Round up
  479.     SHR BX,1            ; Must run on an XT
  480.     SHR BX,1
  481.     SHR BX,1
  482.     SHR BX,1            ; Now have number of paragraphs
  483.     MOV AX,4800H            ; Allocate memory
  484.     INT DOS
  485.      JC INSTFAIL            ; Give fail return
  486.     MOV WORD PTR TBuff[SI],0    ; Save in private block for this port
  487.     MOV WORD PTR TBuff[SI+2],AX
  488.  
  489.     MOV BX,R_SIZE            ; Receive buffer size
  490.     ADD BX,0FH            ; Round up
  491.     SHR BX,1            ; Must run on an XT
  492.     SHR BX,1
  493.     SHR BX,1
  494.     SHR BX,1            ; Now have number of paragraphs
  495.     MOV AX,4800H            ; Allocate memory
  496.     INT DOS
  497.      JNC INSTSUCC            ; Success --> Continue
  498.  
  499.     ; Unhand the send buffer assigned above
  500.  
  501.     MOV AX,WORD PTR TBuff+2[SI]    ; Transmit buffer paragraph
  502.     MOV ES,AX            ; Honest.  That's where the arg goes.
  503.     MOV AX,4900H            ; Release memory
  504.     INT DOS
  505.     ; Ignore error
  506.     ; Fall into INSTFAIL
  507.  
  508. INSTFAIL:
  509.      JMP INST666            ; Failure --> Give failed response
  510.  
  511. INSTSUCC:
  512.     MOV WORD PTR RBuff[SI],0    ; Save in private block for this port
  513.     MOV WORD PTR RBuff[SI+2],AX
  514.  
  515. IFDEF DEBUG
  516.     PUSH DI
  517.     CLD                ; Go up in memory
  518.     XOR AX,AX            ; A zero to store
  519.     LES DI,TBuff[SI]        ; Transmit buffer location
  520.     MOV CX,S_SIZE            ; Size of buffer
  521.     REP STOSB            ; Clear entire buffer
  522.     LES DI,RBuff[SI]        ; Receive buffer location
  523.     MOV CX,R_SIZE            ; Size of buffer
  524.     REP STOSB            ; Clear entire buffer
  525.     POP DI
  526. ENDIF
  527.     PAGE;
  528.  
  529. ; CLEAR ERROR COUNTS
  530.     CLI                ; Stray interrupts cause havoc
  531.     MOV    WORD PTR EOVFLOW[SI],0    ; BUFFER OVERFLOWS
  532.     MOV    WORD PTR EOVRUN[SI],0    ; RECEIVE OVERRUNS
  533.     MOV    WORD PTR EBREAK[SI],0    ; BREAK CHARS
  534.     MOV    WORD PTR EFRAME[SI],0    ; FRAMING ERRORS
  535.     MOV    WORD PTR EPARITY[SI],0    ; PARITY ERRORS
  536.     MOV    WORD PTR EXMIT[SI],0    ; TRANSMISSION ERRORS
  537.     MOV    WORD PTR EDSR[SI],0    ; DATA SET READY ERRORS
  538.     MOV    WORD PTR ECTS[SI],0    ; CLEAR TO SEND ERRORS
  539.  
  540.     MOV    BX,RBDA         ; ROM BIOS DATA AREA
  541.     MOV    ES,BX            ; TO ES
  542.     ASSUME    ES:RBDA
  543.  
  544. ; Map port number (COMx) into IO Address using the RS232_Base[x] table in
  545. ; the BIOS data area.  If any of the ports is missing there should be a
  546. ; zero in the table for this COM port.    BIOS startup routines pack the table
  547. ; so that if you have a COM4 but no COM3, 2E8 will be found in 40:4 and 0
  548. ; will be in 40:6.
  549.  
  550. ; N.B. The exact IO address in 40:x is irrelevant and may well be something
  551. ; other than the "standard" values if specially designed hardware is used.
  552. ; To minimize flack, we will use the standard value if the slot in the table
  553. ; is 0.  The bad side effect of this is that (in the standard losing case of
  554. ; a COM4 but no COM3) both COM3 and COM4 will reference the hardware at 2E8.
  555.  
  556.     CMP    PORT[SI],1        ; PORT 1?
  557.      JE    INST3F8         ; Yes
  558.     CMP    PORT[SI],2        ; PORT 2?
  559.      JE    INST2F8         ; Yes
  560.     CMP    PORT[SI],3        ; PORT 3?
  561.      JE    INST3E8         ; Yes
  562.     CMP    PORT[SI],4        ; PORT 4?
  563.      JE    INST2E8         ; Yes
  564.     INT    20H            ; NOTA. (Caller is screwed up badly)
  565.  
  566. INST3F8:MOV AX,3F8H            ; Standard COM1 location
  567.     CMP    RS232_BASE+0,0000H    ; We have information?
  568.      JE    INST2            ; No --> Use default
  569.     MOV    AX,RS232_BASE+0     ; Yes --> Use provided info
  570.     JMP    SHORT INST2        ; CONTINUE
  571.  
  572. INST2F8:MOV AX,2F8H            ; Standard COM2 location
  573.     CMP    RS232_BASE+2,0000H    ; We have information?
  574.      JE    INST2            ; No --> Use default
  575.     MOV    AX,RS232_BASE+2     ; Yes --> Use provided info
  576.     JMP    SHORT INST2        ; CONTINUE
  577.  
  578. INST3E8:MOV AX,3E8H            ; Standard COM3 location
  579.     CMP    RS232_BASE+4,0000H    ; We have information?
  580.      JE    INST2            ; No --> Use default
  581.     MOV    AX,RS232_BASE+4     ; Yes --> Use provided info
  582.     JMP    SHORT INST2        ; CONTINUE
  583.  
  584. INST2E8:MOV AX,2E8H            ; Standard COM4 location
  585.     CMP    RS232_BASE+6,0000H    ; We have information?
  586.      JE    INST2            ; No --> Use default
  587.     MOV    AX,RS232_BASE+6     ; Yes --> Use provided info
  588.     ; Fall into INST2
  589.  
  590.  
  591. ; Now we have an IO address for the COMx that we want to use.  If it is
  592. ; anywhere in RS232_Base, we know that it has been check and is OK to use.
  593. ; So, even if my 2E8 (COM4) appears in 40:6 (normally for COM3), I can use
  594. ; it.
  595.  
  596. INST2:    CMP    AX,RS232_BASE        ; INSTALLED?
  597.      JE    INST2A            ; JUMP IF SO
  598.     CMP    AX,RS232_BASE+2     ; INSTALLED?
  599.      JE    INST2A            ; JUMP IF SO
  600.     CMP    AX,RS232_BASE+4     ; INSTALLED?
  601.      JE    INST2A            ; JUMP IF SO
  602.     CMP    AX,RS232_BASE+6     ; INSTALLED?
  603.      JNE    INST666         ; JUMP IF NOT
  604.     ; Fall into INST2A
  605.  
  606. INST2A: MOV    BX,DATREG        ; OFFSET OF TABLE OF PORTS
  607.     MOV    CX,7            ; LOOP SIX TIMES
  608. INST3:    MOV    WORD PTR [SI][BX],AX    ; SET PORT ADDRESS
  609.     INC    AX            ; NEXT PORT
  610.     ADD    BX,2            ; NEXT WORD ADDRESS
  611.      LOOP    INST3            ; RS232 BASE LOOP
  612.     MOV DX,FCR[SI]            ; Get FIFO Control Register
  613.     MOV AL,FIFO_INIT
  614.     OUT DX,AL            ; Try to initialize the FIFO
  615.     CALL SPINLOOP            ; Permit I/O bus to settle
  616.     MOV DX,IIR[SI]            ; Get interrupt ID register
  617.     IN AL,DX            ; See how the UART responded
  618.     AND AL,FIFO_ENABLED        ; Keep only these bits
  619.     MOV CX,1            ; Assume chunk size of 1 for 8250 case
  620.     CMP AL,FIFO_ENABLED        ; See if 16550A
  621.      JNE INST4            ; Jump if not
  622.     MOV CX,FIFO_LEN
  623. INST4:    MOV UART_SILO_LEN[SI],CL    ; Save chunk size for XMIT side only
  624.     MOV AL,FIFO_CLEAR
  625.     OUT DX,AL
  626.  
  627.     MOV    AH,25H            ; SET INTERRUPT VECTOR CONTENTS
  628.     MOV    AL,INT_COM[SI]        ; INTERRUPT NUMBER
  629.     MOV    DX,INT_HNDLR[SI]    ; OUR INTERRUPT HANDLER [WWP]
  630.     PUSH    DS            ; SAVE DATA SEGMENT
  631.     PUSH    CS            ; COPY CS
  632.     POP    DS            ; TO DS
  633.     INT    DOS            ; DOS FUNCTION
  634.     POP    DS            ; RECOVER DATA SEGMENT
  635.  
  636. ; PORT INSTALLED
  637. INST9:    MOV AX,1
  638.     JMP SHORT INSTX
  639.  
  640. ; PORT NOT INSTALLED
  641. INST666:MOV AX,0
  642.     ;Fall into INSTX
  643.  
  644. ; Common exit
  645. INSTX:    MOV INSTALLED[SI],AL        ; Indicate whether installed or not
  646. IFDEF DEBUG
  647.     MOV DX,ST8250
  648.     CMP UART_SILO_LEN[SI],1
  649.      JE INSTXX
  650.     MOV DX,ST16550
  651. INSTXX: MOV AH,9
  652.     INT DOS             ; Announce UART type
  653.     MOV DX,STUART
  654.     INT DOS
  655. ENDIF
  656.     POP ES
  657.     POP DI
  658.     POP SI
  659.     POPF                ; Restore caller's interrupt state
  660.     mov sp,bp
  661.     pop bp
  662.     RET
  663. _install_com ENDP
  664.     PAGE;
  665. ;
  666. ; void far restore_com(void)
  667. ;    Restore original interrupt vector and release storage
  668. ;
  669. _restore_com PROC FAR
  670.     push bp
  671.     mov bp,sp
  672.     PUSHF
  673.     PUSH SI
  674.     PUSH ES
  675.     MOV    SI,CURRENT_AREA     ; SI POINTS TO DATA AREA
  676.     CLI
  677.     MOV    INSTALLED[SI],0     ; PORT IS NO LONGER INSTALLED
  678.     MOV    AH,25H            ; SET INTERRUPT VECTOR FUNCTION
  679.     MOV    AL,INT_COM[SI]        ; INTERRUPT NUMBER
  680.     MOV    DX,OLD_COM_OFF[SI]    ; OLD OFFSET TO DX
  681.     MOV    BX,OLD_COM_SEG[SI]    ; OLD SEG
  682.     PUSH    DS            ; SAVE DS
  683.     MOV    DS,BX            ; TO DS
  684.     INT    DOS            ; DOS FUNCTION
  685.     POP DS                ; Recover our data segment
  686.  
  687.     MOV AX,WORD PTR TBuff+2[SI]    ; Transmit buffer paragraph
  688.     MOV ES,AX            ; Honest.  That's where the arg goes.
  689.     MOV AX,4900H            ; Release memory
  690.     INT DOS
  691.      ; Ignore error
  692.     MOV AX,WORD PTR RBuff+2[SI]    ; Receive buffer paragraph
  693.     MOV ES,AX
  694.     MOV AX,4900H
  695.     INT DOS
  696.      ; Ignore error
  697.     POP ES
  698.     POP SI
  699.     POPF
  700.     mov sp,bp
  701.     pop bp
  702.     RET
  703. _restore_com ENDP
  704.     PAGE;
  705. ;
  706. ; void far open_com(int Baud, char Conn, char Parity, char Stops, char Flow);
  707. ;
  708. ; CLEAR BUFFERS
  709. ; RE-INITIALIZE THE UART
  710. ; ENABLE INTERRUPTS ON THE 8259 INTERRUPT CONTROL CHIP
  711. ;
  712. ; [bp+6] = BAUD RATE
  713. ; [bp+8] = CONNECTION: M(ODEM), D(IRECT)
  714. ; [bp+10] = PARITY:    N(ONE), O(DD), E(VEN), S(PACE), M(ARK)
  715. ; [bp+12] = STOP BITS:    1, 2
  716. ; [bp+14] = XON/XOFF:    E(NABLED), D(ISABLED)
  717. ;
  718. _open_com PROC FAR
  719.     push bp
  720.     mov bp,sp
  721.     PUSHF
  722.     PUSH SI
  723.     MOV    SI,CURRENT_AREA     ; SI POINTS TO DATA AREA
  724.     CLI                ; INTERRUPTS OFF
  725.     TEST INSTALLED[SI],1        ; Port installed?
  726.      JNZ OPEN1            ; Yes --> Proceed
  727.      JMP OPENX            ; No  --> Get out
  728.  
  729. OPEN1:    mov ax,[bp+6]
  730.     MOV    BAUD_RATE[SI],AX    ; SET
  731.     mov bh,[bp+8]
  732.     MOV    CONNECTION[SI],BH    ; ARGS
  733.     mov bl,[bp+10]
  734.     MOV    PARITY[SI],BL        ; IN
  735.     mov ch,[bp+12]
  736.     MOV    STOP_BITS[SI],CH    ; MEMORY
  737.     mov cl,[bp+14]
  738.     MOV    XON_XOFF[SI],CL
  739.  
  740. ; RESET FLOW CONTROL
  741.     MOV    HOST_OFF[SI],0        ; HOST FLOWING
  742.     MOV    PC_OFF[SI],0        ; PC FLOWING
  743.     MOV URGENT_SEND[SI],0        ; No (high priority) flow ctl
  744.     MOV SEND_OK[SI],0        ; DTR&CTS are not on yet
  745.  
  746. ; RESET BUFFER COUNTS AND POINTERS
  747.     MOV    START_TDATA[SI],0
  748.     MOV    END_TDATA[SI],0
  749.     MOV    START_RDATA[SI],0
  750.     MOV    END_RDATA[SI],0
  751.     MOV    SIZE_TDATA[SI],0
  752.     MOV    SIZE_RDATA[SI],0
  753.  
  754. ;
  755. ; RESET THE UART
  756.     MOV DX,MCR[SI]            ; Modem Control Register
  757.     IN AL,DX            ; Get current settings
  758.     AND AL,0FEH            ; Clr RTS, OUT1, OUT2 & LOOPBACK, but
  759.     OUT DX,AL            ; Not DTR (No hangup during autobaud)
  760.     MOV DX,MSR[SI]            ; Modem Status Register
  761.     IN AL,DX            ; Get current DSR and CTS states.
  762.     AND AL,30H            ; Init PREVIOUS STATE FLOPS to current
  763.     OUT DX,AL            ;  state and clear Loopback, etc.
  764.     IN AL,DX            ; Re-read to get delta bits & clr int
  765.     AND AL,30H            ; Leave the two critical bits
  766.     CMP AL,30H            ; Both on?
  767.      JNE OPEN2            ; No.  Leave SEND_OK zero.
  768.     MOV SEND_OK[SI],1        ; Allow TXI to send out data
  769. OPEN2:    MOV DX,FCR[SI]            ; I/O Address of FIFO control register
  770.     MOV AL,FIFO_CLEAR        ; Disable FIFOs
  771.     OUT DX,AL            ; Non-16550A chips will ignore this
  772.     MOV    DX,LSR[SI]        ; RESET LINE STATUS CONDITION
  773.     IN    AL,DX
  774.     MOV    DX,DATREG[SI]        ; RESET RECEIVE DATA CONDITION
  775.     IN    AL,DX
  776.     MOV    DX,MSR[SI]        ; RESET MODEM DELTAS AND CONDITIONS
  777.     IN    AL,DX
  778.  
  779.     CALL Set_Baud            ; Set the baud rate from arg
  780.     PAGE;
  781. ; SET PARITY AND NUMBER OF STOP BITS
  782.     MOV    AL,03H            ; Default: NO PARITY + 8 bits data
  783.  
  784.     CMP    PARITY[SI],'O'          ; ODD PARITY REQUESTED?
  785.      JNE    P1            ; JUMP IF NOT
  786.     MOV    AL,0AH            ; SELECT ODD PARITY + 7 bits data
  787.     JMP    SHORT P4        ; CONTINUE
  788. ;
  789. P1:    CMP    PARITY[SI],'E'          ; EVEN PARITY REQUESTED?
  790.      JNE    P2            ; JUMP IF NOT
  791.     MOV    AL,1AH            ; SELECT EVEN PARITY + 7 bits data
  792.     JMP    SHORT P4        ; CONTINUE
  793. ;
  794. P2:    CMP    PARITY[SI],'M'          ; MARK PARITY REQUESTED?
  795.      JNE    P3            ; JUMP IF NOT
  796.     MOV    AL,2AH            ; SELECT MARK PARITY + 7 bits data
  797.     JMP SHORT P4
  798.  
  799. P3:    CMP PARITY[SI],'S'              ; SPACE parity requested?
  800.      JNE P4             ; No.  Must be 'N' (NONE)
  801.     MOV AL,3AH            ; Select SPACE PARITY + 7 bits data
  802.  
  803. P4:    TEST    STOP_BITS[SI],2     ; 2 STOP BITS REQUESTED?
  804.      JZ    STOP1            ; NO
  805.     OR    AL,4            ; YES
  806. STOP1:    MOV    DX,LCR[SI]        ; LINE CONTROL REGISTER
  807.     OUT    DX,AL            ; SET UART PARITY MODE AND DLAB=0
  808.  
  809. ; Initialize the FIFOs
  810.     MOV    DX,FCR[SI]        ; I/O Address of FIFO control register
  811.     MOV    AL,FIFO_INIT        ; Clear FIFOs, set size, enable FIFOs
  812.     OUT    DX,AL            ; Non-16550A chips will ignore this
  813.  
  814. ; ENABLE INTERRUPTS ON 8259 AND UART
  815.     IN    AL,INTA01        ; SET ENABLE BIT ON 8259
  816.     AND    AL,NIRQ[SI]
  817.     OUT    INTA01,AL
  818.     MOV DX,IER[SI]            ; Interrupt enable register
  819.     MOV AL,0DH            ; Line & Modem status, recv [GT]
  820.     OUT DX,AL            ; Enable those interrupts
  821.  
  822. OPENX:    POP SI
  823.     POPF                ; Restore interrupt state
  824.     mov sp,bp
  825.     pop bp
  826.     RET                ; DONE
  827. _open_com ENDP
  828.     PAGE;
  829. ;
  830. ; void far ioctl_com(int Flags, int Arg1, ...)
  831. ;    Flags have bits saying what to do or change (IGNORED TODAY)
  832. ;    Arg1, ...  are the new values
  833. ;
  834. _ioctl_com PROC FAR
  835.     PUSH BP
  836.     MOV BP,SP
  837.     PUSHF                ; Save interrupt context
  838.     PUSH SI
  839.     MOV SI,CURRENT_AREA        ; Pointer to COMi private area
  840.     CLI                ; Prevent surprises
  841.     TEST INSTALLED[SI],1
  842.      JE IOCTLX            ; No good.  Just return.
  843.     MOV AX,[BP+6]            ; Flags
  844.     ; Check bits here...
  845.     MOV AX,[BP+8]            ; Line speed
  846.     MOV BAUD_RATE[SI],AX        ; Save in parameter block
  847.     CALL Set_Baud            ; Set the baud rate in UART
  848. IOCTLX: POP SI
  849.     POPF                ; Restore interrupt state
  850.     MOV SP,BP
  851.     POP BP
  852.     RET
  853. _ioctl_com    ENDP
  854.     PAGE;
  855. ; ioctl-called routines (internal) ...
  856.  
  857. ; SI:    COMi private block
  858. ;    CALL Set_Baud
  859. ; Returns: (nothing)
  860. ; Clobber: AX, BX, DX
  861.  
  862. Set_Baud PROC NEAR
  863.     MOV AX,50
  864.     MUL DIV50            ; Could be different on a PCJr!
  865.     DIV BAUD_RATE[SI]        ; Get right number for the UART
  866.     MOV BX,AX            ; Save it
  867.     MOV DX,LCR[SI]            ; Line Control Register
  868.     IN AL,DX            ; Get current size, stops, parity,...
  869.     PUSH AX
  870.     OR AL,80H            ; DLAB bit
  871.     OUT DX,AL            ; Talk to the baud rate regs now
  872.     MOV DX,WORD PTR DLL[SI]     ; Least significant byte
  873.     MOV AL,BL            ; New value
  874.     OUT DX,AL            ; To UART
  875.     MOV DX,WORD PTR DLH[SI]     ; Most signifiant byte
  876.     MOV AL,BH            ; New value
  877.     OUT DX,AL
  878.     MOV DX,LCR[SI]            ; Line Control Register
  879.     POP AX
  880.     OUT DX,AL            ; Turn off DLAB, keep saved settings
  881.     RET
  882. Set_Baud ENDP
  883.     PAGE;
  884. ;
  885. ; void far close_com(void)
  886. ;    Turn off interrupts from the COM port
  887. ;
  888. _close_com PROC FAR
  889.     push bp
  890.     mov bp,sp
  891.     PUSHF
  892.     PUSH SI
  893.     MOV    SI,CURRENT_AREA     ; SI POINTS TO DATA AREA
  894.     TEST    INSTALLED[SI],1     ; PORT INSTALLED?
  895.      JZ    CCX            ; ABORT IF NOT
  896.  
  897. ; TURN OFF UART and clear FIFOs in NS16550A
  898.     CLI
  899.     MOV DX,IER[SI]
  900.     MOV AL,0
  901.     OUT DX,AL            ; No interrupts right now, please
  902.     MOV DX,FCR[SI]            ; FIFO Control Register
  903.     MOV AL,FIFO_CLEAR        ; Disable FIFOs
  904.     OUT DX,AL
  905.     MOV DX,MCR[SI]            ; Modem control register
  906.     XOR AL,AL            ; OUT2 is IRQ enable on some machines,
  907.     OUT DX,AL            ; So, clear RTS, OUT1, OUT2, LOOPBACK
  908.  
  909. ; TURN OFF 8259
  910.     MOV    DX,INTA01
  911.     IN    AL,DX
  912.     OR    AL,IRQ[SI]
  913.     JMP    $+2            ; DELAY FOR AT
  914.     JMP    $+2            ; DELAY FOR AT
  915.     JMP    $+2            ; DELAY FOR AT
  916.     OUT    DX,AL
  917.  
  918. CCX:    POP SI
  919.     POPF                ; Restore interrupt state
  920.     mov sp,bp
  921.     pop bp
  922.     RET
  923. _close_com ENDP
  924.     PAGE;
  925. ;
  926. ; void far dtr_off(void)
  927. ;    Tells modem we are done.  Remote end should hang up also.
  928. ;
  929. _dtr_off PROC FAR
  930.     push bp
  931.     mov bp,sp
  932.     PUSH SI
  933.     MOV    SI,CURRENT_AREA     ; SI POINTS TO DATA AREA
  934.     TEST    INSTALLED[SI],1     ; PORT INSTALLED?
  935.      JZ    DFX            ; ABORT IF NOT
  936.  
  937.     MOV DX,MCR[SI]            ; Modem Control Register
  938.     IN AL,DX            ; Get current state
  939.     PUSH AX             ; Save MCR
  940.     MOV AL,08H            ; DTR off, RTS off, OUT2 on
  941.     OUT DX,AL
  942.     POP AX                ; Get previous state
  943.     AND AL,1            ; Just look at the DTR bit
  944.      JE DFX             ; Not on.  Don't clr.  Don't wait.
  945.     MOV AX,50            ; 50/100 of second
  946. IFNDEF UUPC
  947.     CALL WaitN            ; V.24 says it must be low >1/2 sec
  948. ENDIF
  949. DFX:    POP SI
  950.     mov sp,bp
  951.     pop bp
  952.     RET
  953. _dtr_off    ENDP
  954.     PAGE;
  955. ;
  956. ; void far dtr_on(void)     Tell modem we can take traffic
  957. ;
  958. _dtr_on PROC FAR
  959.     push bp
  960.     mov bp,sp
  961.     PUSH SI
  962.     MOV    SI,CURRENT_AREA     ; SI POINTS TO DATA AREA
  963.     TEST    INSTALLED[SI],1     ; PORT INSTALLED?
  964.      JZ DTRONF            ; Suppress output if not
  965.  
  966. ; Tell modem we are ready and want to send with line idle
  967.  
  968.     MOV DX,MCR[SI]            ; Modem Control Register
  969.     MOV AL,00001011B        ; OUT 2, RTS, DTR
  970.     OUT DX,AL
  971.     CMP CONNECTION[SI],'D'          ; Direct connection (no DSR,CTS)?
  972.      JNE DTRON0            ; Go wait for DSR, CTS
  973.     MOV SEND_OK[SI],1        ; Set output enable flag
  974.     JMP SHORT DTRONS        ; Give success return
  975.  
  976. ; Wait for awhile to give the modem time to respond
  977.  
  978. DTRON0: MOV AH,2CH            ; Get time (H:M:S:H to CH:CL:DH:DL)
  979.     INT 21H
  980.     MOV BX,DX            ; Save seconds&hundreths
  981.     ADD BH,06            ; Allow a few seconds
  982.     CMP BH,60            ; Wrap around check
  983.      JL DTRON1            ; No wrap
  984.     SUB BH,60
  985. DTRON1: CMP SEND_OK[SI],1        ; Did the modem come up?
  986.      JE DTRONS            ; Yes.    Both DSR and CTS are true.
  987.     INT 21H             ; Get the time again
  988.     CMP DX,BX            ; Current time is passed the deadline?
  989.      JB DTRON1            ; No, keep checking 'til time runs out
  990.  
  991.     ; Modem failed to come up.  Bump counts that tell why.
  992.     MOV    DX,MSR[SI]        ; MODEM STATUS REGISTER
  993.     IN    AL,DX            ; GET MODEM STATUS
  994.     TEST    AL,20H            ; DATA SET READY?
  995.      JNZ DTRON6            ; Yup.
  996.     INC    WORD PTR EDSR[SI]    ; BUMP ERROR COUNT
  997. DTRON6: TEST    AL,10H            ; Clear To Send?
  998.      JNZ DTRONF            ; That's OK.
  999.     INC    WORD PTR ECTS[SI]    ; BUMP ERROR COUNT - WE TIMED OUT
  1000.     ; Fall into DTRONF
  1001.     PAGE;
  1002. ; Failure return
  1003.  
  1004. DTRONF: MOV SEND_OK[SI],1        ; Make believe DSR & CTS are up!!!
  1005.     MOV CONNECTION[SI],'D'          ; Switch to DIR connection (MSTATINT)
  1006.     JMP SHORT DTRONX
  1007.  
  1008.  
  1009. ; Successful return
  1010.  
  1011. DTRONS: ; SEND_OK is on.  Setting it again could confuse interrupt level
  1012.     ; Fall into DTRONX
  1013.  
  1014. DTRONX: MOV AX,200H            ; 2 Seconds
  1015. IFNDEF UUPC
  1016.     CALL WaitN            ; V.24 says 2 sec hi before data
  1017. ENDIF
  1018.     POP SI
  1019.     mov sp,bp
  1020.     pop bp
  1021.     RET
  1022. _dtr_on ENDP
  1023.     PAGE;
  1024. ;
  1025. ; Wait for specified time using the 18.2 ticks/second clock
  1026. ;
  1027. ; Call:     AX has seconds,hundreths
  1028. ;        CALL WaitN
  1029. ; Return:    At least the requested time has passed
  1030. ;
  1031.  
  1032. WaitN    PROC NEAR
  1033.     PUSH BP
  1034.     MOV BP,SP
  1035.     PUSH AX
  1036.     PUSH BX
  1037.     PUSH CX
  1038.     PUSH DX
  1039.     PUSH AX             ; Save a copy of the arg
  1040.     MOV AH,2CH            ; Get time (H:M:S:H to CH:CL:DH:DL)
  1041.     INT DOS
  1042.     POP BX                ; Recover S:H arg
  1043.     ADD BX,DX            ; Determine deadline
  1044.     CMP BL,100            ; Wrap around?
  1045.      JL WaitN1            ; No
  1046.     SUB BL,100            ; Yes.    Subtract 100 hundreths
  1047.     INC BH                ; And add a second
  1048. WaitN1: CMP BH,60            ; Wrap around check
  1049.      JL WaitN2            ; No wrap
  1050.     SUB BH,60            ; Forget about Days and Hours
  1051. WaitN2: INT DOS             ; Get the time again
  1052.     CMP DX,BX            ; Is current time after the deadline?
  1053.      JB WaitN2            ; No, keep checking 'til time runs out
  1054.     POP DX
  1055.     POP CX
  1056.     POP BX
  1057.     POP CX
  1058.     MOV SP,BP
  1059.     POP BP
  1060.     RET
  1061. WaitN    ENDP
  1062.     PAGE;
  1063. ;
  1064. ; unsigned long r_count(void)
  1065. ;    Value is really two uints:  Buffer size in high half, count in low.
  1066. ;    Count returned is <= number of chars waiting to be read.
  1067. ;        (More may come in after you asked.)
  1068. ;
  1069. _r_count PROC FAR
  1070.     push bp
  1071.     mov bp,sp
  1072.     PUSH SI
  1073.     MOV    SI,CURRENT_AREA     ; SI POINTS TO DATA AREA
  1074.     XOR AX,AX            ; Say nothing available if not inst'd
  1075.     MOV DX,R_SIZE            ; Size of entire receive buffer
  1076.     TEST    INSTALLED[SI],1     ; PORT INSTALLED?
  1077.      JZ    RCX            ; ABORT IF NOT
  1078.     MOV    AX,SIZE_RDATA[SI]    ; GET NUMBER OF BYTES USED
  1079. RCX:    POP SI
  1080.     mov sp,bp
  1081.     pop bp
  1082.     RET
  1083. _r_count ENDP
  1084.     PAGE;
  1085. ;
  1086. ; char far receive_com(void)
  1087. ;    Returns AX: -1 if port not installed or no characters available
  1088. ;        or AX: the next character with parity stipped if not in P mode
  1089. ;
  1090. _receive_com PROC FAR
  1091.     push bp
  1092.     mov bp,sp
  1093.     PUSHF                ; Save interrupt state
  1094.     PUSH SI
  1095.     PUSH ES
  1096.     MOV    SI,CURRENT_AREA     ; SI POINTS TO DATA AREA
  1097.     mov    ax,-1            ; -1 if bad call
  1098.     TEST    INSTALLED[SI],1     ; PORT INSTALLED?
  1099.      JZ    RECVX            ; ABORT IF NOT
  1100.     CLI
  1101.     CMP    SIZE_RDATA[SI],0    ; ANY CHARACTERS?
  1102.      JE RECVX            ; Return -1 in AX
  1103.  
  1104.     mov ah,0            ; good call
  1105.     LES    BX,RBuff[SI]        ; Location of receive buffer
  1106.     ADD    BX,START_RDATA[SI]    ; GET POINTER TO OLDEST CHAR
  1107.     MOV AL,ES:[BX]            ; Get character from buffer
  1108.     CMP    PARITY[SI],'N'          ; ARE WE RUNNING WITH NO PARITY? LBA
  1109.      JE    RECV1            ; IF SO, DON'T STRIP HIGH BIT    LBA
  1110.     AND    AL,7FH            ; STRIP PARITY BIT
  1111. RECV1:    MOV BX,START_RDATA[SI]        ; Get the start index again
  1112.     INC    BX            ; BUMP START_RDATA
  1113.     AND BX,R_SIZE-1         ; Ring the pointer
  1114.     MOV    START_RDATA[SI],BX    ; SAVE THE NEW START_RDATA VALUE
  1115.     DEC    SIZE_RDATA[SI]        ; ONE LESS CHARACTER
  1116.     CMP    XON_XOFF[SI],'E'        ; FLOW CONTROL ENABLED?
  1117.      JNE    RECVX            ; DO NOTHING IF DISABLED
  1118.     CMP    HOST_OFF[SI],1        ; HOST TURNED OFF?
  1119.      JNE    RECVX            ; JUMP IF NOT
  1120.     CMP    SIZE_RDATA[SI],R_SIZE/16; RECEIVE BUFFER NEARLY EMPTY?
  1121.      JGE    RECVX            ; DONE IF NOT
  1122.     MOV    HOST_OFF[SI],0        ; TURN ON HOST IF SO
  1123.  
  1124.     PUSH    AX            ; SAVE RECEIVED CHAR
  1125.     MOV    AL,CONTROL_Q        ; TELL HIM TO TALK
  1126. RECV3:    CLI                ; TURN OFF INTERRUPTS
  1127.     CMP URGENT_SEND[SI],1        ; Previous send still in progress?
  1128.      JNE RECV4            ; No.  There is space now.
  1129.     STI                ; Yes.    Wait for interrupt to take it.
  1130.     JMP SHORT RECV3         ; Loop 'til it's gone
  1131. RECV4:    CALL    SENDII            ; SEND IMMEDIATELY INTERNAL
  1132.     POP    AX            ; RESTORE RECEIVED CHAR
  1133.  
  1134. RECVX:    POP ES
  1135.     POP SI
  1136.     POPF                ; Restore interrupt state
  1137.     mov sp,bp
  1138.     pop bp
  1139.     RET
  1140. _receive_com ENDP
  1141.     PAGE;
  1142. ;
  1143. ; unsigned long s_count(void)
  1144. ;    Value is really two uints: Buffer size in high half (returned in DX).
  1145. ;                Free space count in low (returned in AX).
  1146. ;    Count returned is <= number of chars which can be sent without blocking.
  1147. ;        (More may become available after you asked.)
  1148. ;
  1149. ; N.B. The free space might be negative (-1) if the buffer was full and then
  1150. ; the program called SENDI or RXI required sending a control-S to squelch
  1151. ; the remote sender.  Return 0 in this case.
  1152. ;
  1153. _s_count PROC FAR
  1154.     push bp
  1155.     mov bp,sp
  1156.     PUSH SI
  1157.     MOV    SI,CURRENT_AREA     ; SI POINTS TO DATA AREA
  1158.     MOV    AX,0            ; NO SPACE LEFT IF NOT INSTALLED
  1159.     mov dx,S_SIZE-1         ; Leave 1 byte for a SENDII call
  1160.     TEST    INSTALLED[SI],1     ; PORT INSTALLED?
  1161.      JZ    SCX            ; ABORT IF NOT
  1162.     MOV AX,S_SIZE-1         ; Size, keeping one aside for SENDII
  1163.     SUB AX,SIZE_TDATA[SI]        ; Minus number in use right now
  1164.      JGE SCX            ; Avoid returning negative number
  1165.     XOR AX,AX            ; Return 0
  1166. SCX:    POP SI
  1167.     mov sp,bp
  1168.     pop bp
  1169.     RET
  1170. _s_count ENDP
  1171.     PAGE;
  1172. ;
  1173. ; void far send_com(char)
  1174. ;    Send a character to the selected port
  1175. ;
  1176. _send_com PROC FAR
  1177.     push bp
  1178.     mov bp,sp
  1179.     PUSHF                ; Save interrupt state
  1180.     PUSH SI
  1181.     PUSH ES
  1182.     MOV    SI,CURRENT_AREA     ; SI POINTS TO DATA AREA
  1183.     TEST    INSTALLED[SI],1     ; PORT INSTALLED?
  1184.      JZ    SENDX            ; ABORT IF NOT
  1185.  
  1186. SEND1:    CMP    SIZE_TDATA[SI],S_SIZE-1 ; BUFFER FULL? (Leave room for SENDII)
  1187.      JGE SEND1            ; Wait for interrupts to empty buffer
  1188.     CLI
  1189.     LES BX,TBuff[SI]        ; Pointer to buffer
  1190.     ADD BX,END_TDATA[SI]        ; ES:BX points to free space
  1191.     MOV AL,[BP+6]            ; Character to send
  1192.     MOV ES:[BX],AL            ; Move character to buffer
  1193.     MOV BX,END_TDATA[SI]        ; Get index of end
  1194.     INC    BX            ; INCREMENT END_TDATA
  1195.     AND BX,S_SIZE-1         ; Ring the pointer
  1196.     MOV    END_TDATA[SI],BX    ; SAVE NEW END_TDATA
  1197.     INC    SIZE_TDATA[SI]        ; ONE MORE CHARACTER IN X-MIT BUFFER
  1198.  
  1199.     TEST PC_OFF[SI],1        ; Were we stopped by a ^S from host?
  1200.      JNZ SENDX            ; Yes.    Don't enable interrupts yet.
  1201.     CALL CHROUT            ; Put a character out to the UART
  1202. SENDX:    POP ES
  1203.     POP SI
  1204.     POPF                ; Restore interrupt state
  1205.     mov sp,bp
  1206.     pop bp
  1207.     RET
  1208. _send_com ENDP
  1209.     PAGE;
  1210. ;
  1211. ; void far sendi_com(char)
  1212. ;    Send a character immediately by placing it at the head of the queue
  1213. ;
  1214. _sendi_com PROC FAR
  1215.     push bp
  1216.     mov bp,sp
  1217.     PUSHF                ; Save interrupt state
  1218.     PUSH SI
  1219.     mov al,[bp+6]
  1220.     MOV    SI,CURRENT_AREA     ; SI POINTS TO DATA AREA
  1221.     TEST    INSTALLED[SI],1     ; PORT INSTALLED?
  1222.      JZ SENDIX            ; Return if not
  1223.  
  1224. SENDI3: CLI                ; TURN OFF INTERRUPTS
  1225.     CMP URGENT_SEND[SI],1        ; Previous send still in progress?
  1226.      JNE SENDI4            ; No.  There is space now.
  1227.     STI                ; Yes.    Wait for interrupt to take it.
  1228.     JMP SHORT SENDI3        ; Loop 'til it's gone
  1229.  
  1230. SENDI4: CALL    SENDII            ; CALL INTERNAL SEND IMMEDIATE
  1231.  
  1232. SENDIX: POP SI
  1233.     POPF                ; Restore interrupt state
  1234.     mov sp,bp
  1235.     pop bp
  1236.     RET
  1237. _sendi_com ENDP
  1238.     PAGE;
  1239. ; SENDII(AL, SI)  [internal routine]
  1240. ;    Put char at head of output queue so it will go out next
  1241. ;    Called from process level and (receive) interrupt level
  1242. ;    DEPENDS ON CALLER TO KEEP INTERRUPTS CLEARED AND SET SI
  1243. ;
  1244. SENDII    PROC NEAR
  1245.     PUSH BX
  1246.     PUSH DX
  1247.     PUSH ES
  1248.     LES BX,TBuff[SI]        ; Location of transmit buffer
  1249.     CMP    SIZE_TDATA[SI],S_SIZE    ; BUFFER FULL?
  1250.      JB    SENDII2         ; JUMP IF NOT
  1251.     INC    WORD PTR EOVFLOW[SI]    ; BUMP ERROR COUNT (Can this happen?)
  1252.     ADD BX,START_TDATA[SI]        ; ES:BX point to 1st chr in buffer
  1253.     MOV ES:[BX],AL            ; Overwrite 1st character
  1254.     JMP SHORT SENDII4
  1255.  
  1256. SENDII2:MOV DX,START_TDATA[SI]        ; DX is index of 1st char
  1257.     DEC DX                ; Back it up
  1258.     AND DX,S_SIZE-1         ; Ring it
  1259.     MOV START_TDATA[SI],DX        ; Save new value
  1260.     ADD BX,DX            ; Address within buffer
  1261.     MOV ES:[BX],AL            ; Move character to buffer
  1262.     INC    SIZE_TDATA[SI]        ; ONE MORE CHARACTER IN X-MIT BUFFER
  1263.     MOV URGENT_SEND[SI],1        ; Flag high priority message
  1264.     ; No check for PC_OFF here.  Flow control ALWAYS gets sent!
  1265. SENDII4:CALL CHROUT            ; Output a chr if possible
  1266. SENDIIX:POP ES
  1267.     POP DX
  1268.     POP BX
  1269.     RET
  1270. SENDII    ENDP
  1271.  
  1272. ;*---------------------------------------------------------------------*
  1273. ;*    s p i n l o o p                            *
  1274. ;*                                       *
  1275. ;*    Waste time to allow slow UART chips to catch up            *
  1276. ;*---------------------------------------------------------------------*
  1277.  
  1278. SPINLOOP PROC NEAR
  1279.     PUSH CX
  1280.     MOV CX,100
  1281. WASTERS:
  1282.     CALL CRET        ; Better time waster than a 1-byte NOP
  1283.     LOOP WASTERS
  1284.     POP  CX
  1285. CRET:    RET
  1286. SPINLOOP ENDP
  1287.  
  1288. ; CHROUT()    Process level routine to remove a chr from the buffer,
  1289. ;        give it to the UART and adjust the pointer and count.
  1290. ;        If interrupts are disabled at entry, nothing is done.
  1291. ;        If a character is successfully output, Tx ints are enabled.
  1292. ;    Requires: SI pointing at the appropriate data area
  1293. ;    Clobbers: AX, BX, DX, ES
  1294. ;    Must preserve: CX in case there is a count there
  1295.  
  1296. CHROUT    PROC NEAR
  1297.     MOV DX,IER[SI]            ; Interrupt Enable Register
  1298.     IN AL,DX
  1299.     TEST AL,2            ; Tx interrupts enabled?
  1300.      JNZ CHROUX            ; Jump if not
  1301.     CMP SEND_OK[SI],1        ; See if Data Set Ready & CTS are on
  1302.      JNE CHROUX            ; No. Still can't enable TX ints
  1303.     CALL TX_CHR            ; Actually transmit the chr
  1304.     MOV DX,IER[SI]            ; Interrupt Enable Register
  1305.     MOV AL,0FH            ; Rx, Tx, Line & Modem enable bits
  1306.     OUT DX,AL            ; Enable those interrupts
  1307. CHROUX: RET
  1308. CHROUT    ENDP
  1309.     PAGE;
  1310.  
  1311. IFNDEF UUPC
  1312. ;
  1313. ; void far send_local(char);
  1314. ;    Simulate a loopback by placing characters sent in recv buffer
  1315. ;
  1316. _send_local PROC FAR
  1317.     push bp
  1318.     mov bp,sp
  1319.     PUSHF
  1320.     PUSH SI
  1321.     PUSH ES
  1322.     MOV    SI,CURRENT_AREA     ; SI POINTS TO DATA AREA
  1323.     TEST    INSTALLED[SI],1     ; PORT INSTALLED?
  1324.      JZ    SLX            ; ABORT IF NOT
  1325.  
  1326.     CLI                ; INTERRUPTS OFF
  1327.     CMP    SIZE_RDATA[SI],R_SIZE    ; SEE IF ANY ROOM
  1328.      JB SL3             ; SKIP IF ROOM
  1329.     INC    WORD PTR EOVFLOW[SI]    ; BUMP OVERFLOW COUNT
  1330.     JMP SHORT SLX            ; PUNT
  1331.  
  1332. SL3:    LES BX,RBuff[SI]        ; Receive buffer location
  1333.     ADD BX,END_RDATA[SI]        ; ES:BX POINTS TO FREE SPACE
  1334.     MOV AL,[BP+6]            ; Get the byte to send
  1335.     MOV ES:[BX],AL            ; Put into buffer
  1336.     MOV BX,END_RDATA[SI]        ; Get the end pointer
  1337.     INC    BX            ; INCREMENT END_RDATA POINTER
  1338.     AND BX,R_SIZE-1         ; Ring the pointer
  1339.     MOV    END_RDATA[SI],BX    ; SAVE VALUE
  1340.     INC    SIZE_RDATA[SI]        ; GOT ONE MORE CHARACTER
  1341.  
  1342. SLX:    POP ES
  1343.     POP SI
  1344.     POPF                ; Restore interrupt state
  1345.     mov sp,bp
  1346.     pop bp
  1347.     RET                ; DONE
  1348. _send_local    ENDP
  1349. ENDIF
  1350.     PAGE;
  1351. ;
  1352. ; void far break_com(void)    Send a BREAK out to alert the remote end
  1353. ;
  1354. _break_com PROC FAR
  1355.     push bp
  1356.     mov bp,sp
  1357.     PUSH SI
  1358.  
  1359.     MOV    SI,CURRENT_AREA     ; SI POINTS TO DATA AREA
  1360.     TEST    INSTALLED[SI],1     ; PORT INSTALLED?
  1361.      JZ    BREAKX            ; ABORT IF NOT
  1362.  
  1363.     MOV    DX,LCR[SI]        ; LINE CONTROL REGISTER
  1364.     IN    AL,DX            ; GET CURRENT SETTING
  1365.     OR    AL,40H            ; TURN ON BREAK BIT
  1366.     OUT    DX,AL            ; SET IT ON THE UART
  1367.     MOV AX,25            ; 25/100 of a second
  1368.     CALL WaitN
  1369.     MOV    DX,LCR[SI]        ; LINE CONTROL REGISTER
  1370.     IN    AL,DX            ; GET CURRENT SETTING
  1371.     AND    AL,0BFH         ; TURN OFF BREAK BIT
  1372.     OUT    DX,AL            ; RESTORE LINE CONTROL REGISTER
  1373. BREAKX: POP SI
  1374.     mov sp,bp
  1375.     pop bp
  1376.     RET
  1377. _break_com ENDP
  1378.     PAGE;
  1379. ;
  1380. ; ERROR_STRUCT far *com_errors(void)
  1381. ;    Returns a pointer to the table of error counters
  1382. ;
  1383. _com_errors PROC FAR
  1384.     push bp
  1385.     mov bp,sp
  1386.     PUSH SI
  1387.     MOV SI,CURRENT_AREA        ; Point to block for selected port
  1388.     LEA AX,ERROR_BLOCK[SI]        ; Offset to error counters
  1389.     MOV DX,DS            ; Value is in DX:AX
  1390.     POP SI
  1391.     mov sp,bp
  1392.     pop bp
  1393.     RET
  1394. _com_errors ENDP
  1395.  
  1396.  
  1397.  
  1398.  
  1399.  
  1400.  
  1401. ;
  1402. ; char far modem_status(void)
  1403. ;    Returns the modem status register in AL
  1404. ;
  1405. ; Bits are:    0x80:    Carrier Detect
  1406. ;        0x40:    Ring Indicator
  1407. ;        0x20:    Data Set Ready
  1408. ;        0x10:    Clear To Send
  1409. ;        0x08:    Delta Carrier Detect    (CD changed)
  1410. ;        0x04:    Trailing edge of RI    (RI went OFF)
  1411. ;        0x02:    Delta DSR        (DSR changed)
  1412. ;        0x01:    Delta CTS        (CTS changed)
  1413.  
  1414. _modem_status PROC FAR
  1415.     push bp
  1416.     mov bp,sp
  1417.     PUSH SI
  1418.     MOV SI,CURRENT_AREA        ; Point to block for selected port
  1419.     MOV DX,MSR[SI]            ; IO Addr of Modem Status Register
  1420.     IN AL,DX            ; Get the live value
  1421.     XOR AH,AH            ; Flush unwanted bits
  1422.     POP SI
  1423.     mov sp,bp
  1424.     pop bp
  1425.     RET
  1426. _modem_status ENDP
  1427.     PAGE;
  1428. ;
  1429. ; INT_HNDLR1 - HANDLES INTERRUPTS GENERATED BY COM1:
  1430. ;
  1431. INT_HNDLR1 PROC FAR
  1432.     PUSH SI
  1433.     MOV    SI,OFFSET DGROUP:AREA1    ; DATA AREA FOR COM1:
  1434.     JMP    SHORT INT_COMMON    ; CONTINUE
  1435. ;
  1436. ; INT_HNDLR2 - HANDLES INTERRUPTS GENERATED BY COM2:
  1437. ;
  1438. INT_HNDLR2 PROC FAR
  1439.     PUSH SI
  1440.     MOV    SI,OFFSET DGROUP:AREA2    ; DATA AREA FOR COM2:
  1441.     JMP    SHORT INT_COMMON    ; CONTINUE
  1442. ;
  1443. ; INT_HNDLR3 - HANDLES INTERRUPTS GENERATED BY COM3:
  1444. ;
  1445. INT_HNDLR3 PROC FAR
  1446.     PUSH SI
  1447.     MOV    SI,OFFSET DGROUP:AREA3    ; DATA AREA FOR COM3:
  1448.     JMP    SHORT INT_COMMON    ; CONTINUE
  1449. ;
  1450. ; INT_HNDLR4 - HANDLES INTERRUPTS GENERATED BY COM4:
  1451. ;
  1452. INT_HNDLR4 PROC FAR
  1453.     PUSH SI
  1454.     MOV    SI,OFFSET DGROUP:AREA4    ; DATA AREA FOR COM4:
  1455.     ; Fall into INT_COMMON
  1456.     PAGE;
  1457. ;
  1458. ; BODY OF INTERRUPT HANDLER
  1459. ;
  1460. INT_COMMON: ; SI has been pushed and loaded
  1461.     PUSH AX
  1462.     PUSH BX
  1463.     PUSH CX
  1464.     PUSH DX
  1465.     PUSH DS
  1466.     PUSH ES
  1467.  
  1468.     MOV AX,DGROUP            ; Offsets are relative to DGROUP [WWP]
  1469.     MOV    DS,AX
  1470.  
  1471.  
  1472. ; FIND OUT WHERE INTERRUPT CAME FROM AND JUMP TO ROUTINE TO HANDLE IT
  1473. REPOLL: MOV DX,IIR[SI]            ; Interrupt Identification Register
  1474.     IN AL,DX
  1475.     TEST AL,1            ; Check the "no interrupt present" bit
  1476.      JNZ INT_END            ; ON means we are done
  1477.     MOV BL,AL            ; Put where we can index by it
  1478.     AND BX,000EH            ; Ignore FIFO_ENABLED bits, etc.
  1479.  
  1480.     JMP WORD PTR CS:INT_DISPATCH[BX]; Go to appropriate routine
  1481.  
  1482. INT_DISPATCH:
  1483.     DW MSI                ; 0: Modem status interrupt
  1484.     DW TXI                ; 2: Transmitter interrupt
  1485.     DW RXI                ; 4: Receiver interrupt
  1486.     DW LSI                ; 6: Line status interrupt
  1487.     DW REPOLL            ; 8: (Future use by UART makers)
  1488.     DW REPOLL            ; A: (Future use by UART makers)
  1489.     DW RXI                ; C: FIFO Timeout
  1490.     DW REPOLL            ; E: (Future use by UART makers)
  1491.  
  1492. INT_END: ; Now tell 8259 we handled that IRQ
  1493.  
  1494.     MOV DX,IER[SI]            ; Gordon Lee's cure for dropped ints
  1495.     IN AL,DX            ; Get enabled interrupts
  1496.     MOV AH,AL
  1497.     XOR AL,AL
  1498.     OUT DX,AL            ; Disable UART interrupts
  1499.     MOV AL,EOI[SI]            ; End of Interrupt. With input gone,
  1500.     OUT INTA00,AL            ; Give EOI to 8259 Interrupt ctlr
  1501.     MOV AL,AH            ; Get save interrupt enable bits
  1502.     OUT DX,AL            ; Restore them, maybe causing a
  1503.                     ; transition into the 8259!!!
  1504.     POP ES
  1505.     POP DS
  1506.     POP DX
  1507.     POP CX
  1508.     POP BX
  1509.     POP AX
  1510.     POP SI
  1511.     IRET
  1512.     PAGE;
  1513. ;
  1514. ; Line status interrupt
  1515. ;
  1516. LSI:    MOV DX,LSR[SI]            ; Line status register
  1517.     IN AL,DX            ; Read line status & bump error counts
  1518.     TEST    AL,2            ; OVERRUN ERROR?
  1519.      JZ    LSI1            ; JUMP IF NOT
  1520.     INC    WORD PTR EOVRUN[SI]    ; ELSE BUMP ERROR COUNT
  1521. LSI1:    TEST    AL,4            ; PARITY ERROR?
  1522.      JZ    LSI2            ; JUMP IF NOT
  1523.     INC    WORD PTR EPARITY[SI]    ; ELSE BUMP ERROR COUNT
  1524. LSI2:    TEST    AL,8            ; FRAMING ERROR?
  1525.      JZ    LSI3            ; JUMP IF NOT
  1526.     INC    WORD PTR EFRAME[SI]    ; ELSE BUMP ERROR COUNT
  1527. LSI3:    TEST    AL,16            ; BREAK RECEIVED?
  1528.      JZ    LSI4            ; JUMP IF NOT
  1529.     INC    WORD PTR EBREAK[SI]    ; ELSE BUMP ERROR COUNT
  1530. LSI4:    JMP    REPOLL            ; SEE IF ANY MORE INTERRUPTS
  1531.  
  1532. ;
  1533. ; Modem status interrupt
  1534. ;
  1535. MSI:    MOV DX,MSR[SI]            ; Modem Status Register
  1536.     IN AL,DX            ; Read status & clear interrupt
  1537.     CMP CONNECTION[SI],'D'          ; Direct connection - ignore int
  1538.      JE MSI0            ; Just noise on DSR,CTS pins
  1539.     AND AL,30H            ; Expose CTS and Data Set Ready
  1540.     CMP AL,30H            ; Both on?
  1541.      JE MSI0            ; Yes.    Enable output at TXI
  1542.     XOR AL,AL
  1543.     JMP SHORT MSI1
  1544.  
  1545. MSI0:    MOV AL,1
  1546. MSI1:    MOV SEND_OK[SI],AL        ; Put where TXI and send_com can see
  1547.     MOV DX,IER[SI]            ; Let a TX int happen for thoro chks
  1548.     MOV AL,0FH            ; Line & modem sts, recv, send.
  1549.     OUT DX,AL
  1550.     JMP REPOLL            ; Check for other interrupts
  1551.     PAGE;
  1552. ;
  1553. ; Transmit interrupt
  1554. ;
  1555. TXI:    CMP SEND_OK[SI],1        ; Harware (CTS & DSR on) OK?
  1556.      JNE TXI9            ; No.  Must wait 'til cable right!
  1557.     MOV CX,1            ; Transfer count for flow ctl
  1558.     CMP URGENT_SEND[SI],1        ; Flow control character to send?
  1559.      JE TXI3            ; Yes.    Always send flow control.
  1560.     CMP PC_OFF[SI],1        ; Flow control (XON/XOFF) OK?
  1561.      JE TXI9            ; Stifled & not urgent. Forget it.
  1562.  
  1563. TXI1:    MOV CL,UART_SILO_LEN[SI]    ; MAX size chunk (1 for simple 8250)
  1564.     ; Too bad there is no "Tranmitter FIFO Full" indication!
  1565.     CMP SIZE_TDATA[SI],CX        ; SEE IF ANY MORE DATA TO SEND
  1566.      JG TXI2            ; UART is the limit
  1567.     MOV CX,SIZE_TDATA[SI]        ; Buffer space limited.  Use that.
  1568. TXI2:     JCXZ TXI9            ; No data, disable TX ints
  1569.  
  1570. TXI3:    CALL TX_CHR            ; Transmit a character
  1571.      LOOP TXI3            ; Keep going 'til silo is full
  1572.     MOV URGENT_SEND[SI],0        ; Tell process level we sent flow ctl
  1573.     JMP    REPOLL
  1574.  
  1575. ; IF NO DATA TO SEND, or can't send, RESET TX INTERRUPT AND RETURN
  1576. TXI9:    MOV DX,IER[SI]
  1577.     MOV AL,0DH            ; Line & modem sts, recv, no send.
  1578.     OUT DX,AL
  1579.     JMP REPOLL
  1580.  
  1581.  
  1582. ; TX_CHR    Internal routine used to actually move a character from
  1583. ;        the transmit buffer to the UART and adjust pointers.
  1584. ;        Called from process and interrupt levels with interrutps
  1585. ;        enabled or disabled.
  1586. ;    Requires: SI pointing at data area for this UART
  1587. ;    Clobbers: AX, BX, DX, ES
  1588. ;    Must preserve: CX  (Caller has count here).
  1589.  
  1590. TX_CHR    PROC NEAR
  1591.     LES BX,TBuff[SI]        ; Pointer to buffer
  1592.     ADD BX,START_TDATA[SI]        ; ES:BX points to next char to send
  1593.     MOV AL,ES:[BX]            ; Get character from buffer
  1594.     MOV DX,DATREG[SI]        ; I/O address of data register
  1595.     OUT DX,AL            ; Output the character
  1596.     INC BX                ; Bump the head pointer
  1597.     AND BX,S_SIZE-1         ; Ring it
  1598.     MOV START_TDATA[SI],BX        ; Store back in private block
  1599.     DEC SIZE_TDATA[SI]        ; One fewer in buffer now
  1600.     RET
  1601. TX_CHR    ENDP
  1602.     PAGE;
  1603. ;
  1604. ; Receive interrupt
  1605. ;
  1606. RXI:
  1607. RXI0:    MOV DX,LSR[SI]            ; Line Status Register
  1608.     IN AL,DX            ; Read it
  1609.     TEST AL,1            ; Check the RECV DATA READY bit
  1610.      JE RXIX            ; No more data available
  1611.     MOV    DX,DATREG[SI]        ; UART DATA REGISTER
  1612.     IN AL,DX            ; Get data, clear status
  1613.     CMP    XON_XOFF[SI],'E'        ; FLOW CONTROL ENABLED?
  1614.      JNE RXI2            ; No.  Don't check for XON/XOFF
  1615.  
  1616. ; Check each character for possible flow control (XON/XOFF)
  1617.     AND    AL,7FH            ; STRIP PARITY
  1618.     CMP    AL,CONTROL_S        ; STOP COMMAND RECEIVED?
  1619.      JNE RXI1            ; Jump if not. Might be ^Q though.
  1620.     MOV PC_OFF[SI],1        ; Stop output
  1621.     JMP SHORT RXI0            ; Don't store character
  1622.  
  1623. RXI1:    CMP    AL,CONTROL_Q        ; GO COMMAND RECEIVED?
  1624.      JNE RXI2            ; No.  Not a flow control character
  1625.     MOV PC_OFF[SI],0        ; Enable output
  1626.     JMP SHORT RXI0            ; Don't store character
  1627.  
  1628. ; Have a real data byte.  Store if possible.
  1629. RXI2:    CMP    SIZE_RDATA[SI],R_SIZE    ; SEE IF ANY ROOM
  1630.      JL RXI6            ; CONTINUE IF SO
  1631.     INC    WORD PTR EOVFLOW[SI]    ; BUMP OVERFLOW ERROR COUNT
  1632.     JMP SHORT RXIX
  1633.  
  1634. RXI6:    LES BX,RBuff[SI]        ; Receive buffer location
  1635.     ADD BX,END_RDATA[SI]        ; ES:BX points to free space
  1636.     MOV ES:[BX],AL            ; Put character in buffer
  1637.     INC    SIZE_RDATA[SI]        ; GOT ONE MORE CHARACTER
  1638.     MOV BX,END_RDATA[SI]        ; Get the end index
  1639.     INC BX                ; Bump it passed location just used
  1640.     AND BX,R_SIZE-1         ; Ring the pointer
  1641.     MOV    END_RDATA[SI],BX    ; SAVE VALUE
  1642.  
  1643. ; See if we must tell remote host to stop outputting.
  1644.     CMP    XON_XOFF[SI],'E'        ; FLOW CONTROL ENABLED?
  1645.      JNE RXI0            ; No
  1646.     CMP HOST_OFF[SI],1        ; Already told remote to shut up?
  1647.      JE RXI0            ; Yes.    Don't flood him with ^Ss
  1648.     CMP    SIZE_RDATA[SI],R_SIZE/2 ; RECEIVE BUFFER NEARLY FULL?
  1649.      JLE RXIX            ; No.  No need to stifle remote end
  1650.     ; Would like to wait here for URGENT_SEND to go off if it is on.
  1651.     ; But we need to take a TX interrupt for that to happen.
  1652.     MOV    AL,CONTROL_S        ; TURN OFF HOST IF SO
  1653.     CALL    SENDII            ; SEND IMMEDIATELY INTERNAL
  1654.     MOV    HOST_OFF[SI],1        ; HOST IS NOW OFF
  1655. RXIX:    JMP REPOLL
  1656.  
  1657. INT_HNDLR4 ENDP
  1658. INT_HNDLR3 ENDP
  1659. INT_HNDLR2 ENDP
  1660. INT_HNDLR1 ENDP
  1661. COM_TEXT   ENDS
  1662.        END
  1663.